use anyhow::Result;
use flate2::read::ZlibDecoder;
use flate2::write::ZlibEncoder;
use flate2::{Compress, Compression};
use std::io::{Read, Write};

///stream used for client. encode when write to stream
pub struct EthanStream {
    raw: Vec<u8>,
}

impl EthanStream {
    pub fn new() -> Self {
        Self { raw: vec![] }
    }
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            raw: Vec::with_capacity(capacity),
        }
    }
    pub fn to_bytes(self) -> Vec<u8> {
        self.raw
    }
    pub fn is_empty(&self) -> bool {
        self.raw.is_empty()
    }
}

impl Read for EthanStream {
    fn by_ref(&mut self) -> &mut Self
    where
        Self: Sized,
    {
        self
    }

    fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result<usize> {
        if buf.len() >= self.raw.len() {
            buf.write_all(&self.raw)?;
            let len = self.raw.len();
            self.raw.clear();
            Ok(len)
        } else {
            buf.write_all(&self.raw[..buf.len()])?;
            self.raw = self.raw.split_off(buf.len());
            Ok(buf.len())
        }
    }
}
impl Write for EthanStream {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.raw.extend_from_slice(buf);
        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

pub fn encode_write_to_ethan_stream(data: &[u8], stream: &mut EthanStream) -> Result<()> {
    let mut encoder = ZlibEncoder::new(stream, Compression::default());
    encoder.write_all(&data)?;
    encoder.try_finish()?;
    Ok(())
}
pub fn decode_read_from_ethan_stream(stream: &mut EthanStream) -> Result<Vec<u8>> {
    let mut decoder = ZlibDecoder::new(stream);
    let mut res = vec![];
    decoder.read_to_end(&mut res)?;
    Ok(res)
}
#[cfg(test)]
mod test {
    use anyhow::Result;
    use bytes::buf;

    use super::*;

    #[test]
    pub fn ethan_stream_write_test() {
        let mut stream = EthanStream::with_capacity(1024);
        let buff = [1, 3, 4, 5];
        match stream.write_all(&buff) {
            Ok(_) => {}
            Err(e) => {
                panic!("failed!, {}", e);
            }
        }
        assert_eq!(stream.to_bytes(), &buff);
    }
    #[test]
    pub fn ethan_stream_read_test() -> Result<()> {
        let mut stream = EthanStream::with_capacity(100);
        let buff = [4, 5, 7, 10];
        stream.write_all(&buff)?;
        let mut buff2 = [0u8; 100];
        let n = stream.read(&mut buff2)?;
        assert_eq!(n, buff.len());
        assert_eq!(buff[..], buff2[..n]);
        assert_eq!(stream.is_empty(), true);
        Ok(())
    }

    #[test]
    pub fn ethan_stream_encode_write() -> Result<()> {
        let mut stream = EthanStream::with_capacity(1000);
        let data = vec![1u8, 3, 4, 5, 6];
        encode_write_to_ethan_stream(&data[..], &mut stream)?;
        assert_eq!(stream.is_empty(), false);
        Ok(())
    }

    #[test]
    pub fn ethan_stream_encode_decode() -> Result<()> {
        let mut stream = EthanStream::with_capacity(1000);
        let data = vec![1u8, 3, 4, 5, 6];
        encode_write_to_ethan_stream(&data[..], &mut stream)?;
        let decode_data = decode_read_from_ethan_stream(&mut stream)?;
        assert_eq!(data[..], decode_data[..]);
        dbg!(decode_data);
        Ok(())
    }
}
